<template>
  <div class="voice-recorder">
    <button 
      class="record-btn"
      :class="{ recording: isRecording }"
      :disabled="!isBrowserSupported"
      @click="toggleRecording"
      aria-label="点击开始/结束录音"
    >
      <i class="fa" :class="isRecording ? 'fa-stop' : 'fa-microphone'"></i>
    </button>
    <div v-if="isRecording" class="recording-info">
      <span>{{ recordingTime }}</span>
    </div>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue';

export default {
  name: 'VoiceRecorder',
  emits: ['recordingStatusChange', 'recognitionResult'],
  setup(props, { emit }) {
    const isRecording = ref(false);
    const isBrowserSupported = ref(false);
    const recordingTime = ref('00:00');
    let startTime = 0;
    let timerInterval = null;

    // WebSocket 与音频相关变量
    let websocket = null;
    let audioContext = null;
    let processor = null;
    let microphone = null;
    let stream = null;

    // 检查浏览器兼容性
    const checkBrowserSupport = () => {
      const hasAudio = 'navigator' in window && 'mediaDevices' in navigator && !!navigator.mediaDevices.getUserMedia;
      const hasWS = 'WebSocket' in window;
      isBrowserSupported.value = hasAudio && hasWS;
    };
    // 格式化时间
    const formatTime = (milliseconds) => {
      const seconds = Math.floor(milliseconds / 1000);
      const minutes = Math.floor(seconds / 60);
      const remainingSeconds = seconds % 60;
      return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
    };
    // 更新录音时间
    const updateRecordingTime = () => {
      const elapsed = Date.now() - startTime;
      recordingTime.value = formatTime(elapsed);
    };

    // 建立WebSocket连接
    const connectWebSocket = () => {
      if (websocket && (websocket.readyState === WebSocket.OPEN || websocket.readyState === WebSocket.CONNECTING)) return;
      websocket = new WebSocket('ws://localhost:8081/api/asr');

      websocket.onopen = () => {
        console.log('[VoiceRecorder] WebSocket onopen');
        if (isRecording.value) websocket.send('start');
      };
      websocket.onmessage = (event) => {
        console.log('[VoiceRecorder] WebSocket onmessage:', typeof event.data, event.data);
        emit('recognitionResult', event.data);
      };
      websocket.onclose = () => {};
      websocket.onerror = (error) => {
        console.error('WebSocket错误:', error);
      };
    };

    // 将Float32Array转换为PCM16
    const convertFloat32ToPCM16 = (float32Array) => {
      const buffer = new ArrayBuffer(float32Array.length * 2);
      const view = new DataView(buffer);
      for (let i = 0; i < float32Array.length; i++) {
        const sample = Math.max(-1, Math.min(1, float32Array[i]));
        const int16 = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
        view.setInt16(i * 2, int16, true);
      }
      return buffer;
    };
    // 开始录音
    const startRecording = async () => {
      if (isRecording.value || !isBrowserSupported.value) return;
      try {
        connectWebSocket();

        stream = await navigator.mediaDevices.getUserMedia({
          audio: {
            sampleRate: 16000,
            channelCount: 1,
            echoCancellation: true,
            noiseSuppression: true,
            autoGainControl: true
          }
        });

        audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 16000 });
        microphone = audioContext.createMediaStreamSource(stream);

        processor = audioContext.createScriptProcessor(4096, 1, 1);
        processor.onaudioprocess = (event) => {
          if (isRecording.value && websocket && websocket.readyState === WebSocket.OPEN) {
            const input = event.inputBuffer.getChannelData(0);
            const pcm = convertFloat32ToPCM16(input);
            websocket.send(pcm);
          }
        };

        microphone.connect(processor);
        processor.connect(audioContext.destination);

        isRecording.value = true;
        startTime = Date.now();
        timerInterval = setInterval(updateRecordingTime, 1000);

        if (websocket && websocket.readyState === WebSocket.OPEN) websocket.send('start');
        console.log('[VoiceRecorder] emit recordingStatusChange:', true);
        emit('recordingStatusChange', true);
      } catch (error) {
        console.error('启动录音失败:', error);
        isRecording.value = false;
        emit('recordingStatusChange', false);
        alert('启动录音失败: ' + error.message);
      }
    };

    const stopRecording = () => {
      if (!isRecording.value) return;
      isRecording.value = false;
      console.log('[VoiceRecorder] emit recordingStatusChange:', false);
      emit('recordingStatusChange', false);

      if (stream) {
        stream.getTracks().forEach(track => track.stop());
        stream = null;
      }
      if (processor) { try { processor.disconnect(); } catch {} processor = null; }
      if (microphone) { try { microphone.disconnect(); } catch {} microphone = null; }
      if (audioContext) { audioContext.close(); audioContext = null; }
      if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }

      if (websocket && websocket.readyState === WebSocket.OPEN) {
        try { websocket.send('stop'); } catch {}
        websocket.close();
      }
    };

    const toggleRecording = () => {
      if (isRecording.value) stopRecording();
      else startRecording();
    };

    onMounted(() => {
      console.log('[VoiceRecorder] mounted');
      checkBrowserSupport();
    });

    onUnmounted(() => {
      if (isRecording.value) stopRecording();
      if (websocket) { try { websocket.close(); } catch {} websocket = null; }
    });

    return {
      isRecording,
      isBrowserSupported,
      recordingTime,
      toggleRecording
    };
  }
};
</script>

<style scoped>
.voice-recorder { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.record-btn { padding: 10px 16px; border-radius: 20px; border: none; background: #1976d2; color: #fff; cursor: pointer; }
.record-btn.recording { background: #dc3545; }
.recording-info { font-size: 14px; color: #1976d2; }
</style>